package cn.changeforyou.web.cloud.auth.manager.impl;

import cn.changeforyou.web.cloud.auth.entity.*;
import cn.changeforyou.web.cloud.auth.exception.AuthExceptionEnum;
import cn.changeforyou.web.cloud.auth.manager.LoginManager;
import cn.changeforyou.web.cloud.auth.model.UserAuthBO;
import cn.changeforyou.web.cloud.auth.service.*;
import cn.changeforyou.web.cloud.auth.util.AuthUtil;
import cn.changeforyou.web.cloud.auth.util.JwtTokenUtil;
import cn.changeforyou.web.cloud.auth.vo.LoginReq;
import cn.changeforyou.web.cloud.auth.vo.LoginVO;
import cn.changeforyou.web.cloud.auth.vo.UserInfoVO;
import cn.changeforyou.web.cloud.webBase.auth.*;
import cn.changeforyou.base.exception.DataExceptionEnum;
import cn.changeforyou.base.exception.ExceptionFactory;
import cn.changeforyou.web.cloud.webBase.utils.bean.BeanCopierUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.jsonwebtoken.Claims;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Log4j2
public class LoginManagerImpl implements LoginManager {

    private UserService userService;
    private UserRoleInfoService userRoleInfoService;
    private OrgInfoService orgInfoService;
    private RoleInfoService roleInfoService;
    private SourceRoleControlService sourceRoleControlService;
    private SourceOrgControlService sourceOrgControlService;
    private SourceInfoService sourceInfoService;
    private JwtTokenUtil jwtTokenUtil;
    private MenuService menuService;

    @Override
    public LoginVO login(LoginReq loginReq) {
        String username = loginReq.getUsername();
        if (StringUtils.isNotBlank(username)) {
            User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username));
            if (null == user) {
                throw ExceptionFactory.jsonException(AuthExceptionEnum.NOT_FOUND_USER);
            }
            String password = user.getPassword();
            if (Objects.equals(AuthUtil.encryptPassword(loginReq.getPassword()), password)) {
                String token = generateToken(user);
                LoginVO loginVO = new LoginVO();
                loginVO.setUserId(user.getId());
                loginVO.setToken(token);
                return loginVO;
            }
            throw ExceptionFactory.jsonException(AuthExceptionEnum.PASSWORD_ERROR);
        }
        throw ExceptionFactory.jsonException(DataExceptionEnum.PARAM_BLANK);
    }

    @Override
    public String generateToken(User user) {
        AuthModel authModel = getAuthModel(user);
        Map<String, Object> map = BeanCopierUtil.beanToMap(authModel);
        return jwtTokenUtil.generateToken(map);
    }

    @Override
    public AuthModel parseToken(String token) {
        Claims claim = jwtTokenUtil.getClaimsFromToken(token);
        try {
            return BeanCopierUtil.mapToBeanWithJackson(claim, AuthModel.class);
        } catch (Exception e) {
            log.error(e);
            throw ExceptionFactory.jsonException(AuthExceptionEnum.TOKEN_ERROR);
        }
    }

    @Override
    public UserInfoVO getUserIngoVO() {
        AuthModel authModel = AuthContainer.getAuthModel();
        UserModel user = authModel.getUserModel();
        RoleModel role = authModel.getRoleModel();
        OrgModel org = authModel.getOrgModel();
        UserInfoVO vo = new UserInfoVO();
        BeanCopierUtil.copy(role, vo);
        BeanCopierUtil.copy(org, vo);
        BeanCopierUtil.copy(user, vo);
        vo.setUserId(user.getId());
        vo.setRoleId(role.getId());
        vo.setOrgId(org.getId());
        return vo;
    }

    @Override
    public List<Menu> queryUserMenus() {
        return menuService.list();
    }

    public AuthModel getAuthModel(User user){
        UserAuthBO userAuthBO = getUserAuthRes(user);
        AuthModel authModel = new AuthModel();

        authModel.setUserModel(userAuthBO.getUserModel());
        authModel.setOrgModel(userAuthBO.getOrgModel());
        authModel.setRoleModel(userAuthBO.getRoleModel());
        authModel.setOrgModel(userAuthBO.getOrgModel());
        authModel.setSourceIds(userAuthBO.getSourceInfos().stream().map(SourceInfo::getId).collect(Collectors.toList()));

        return authModel;
    }

    public UserAuthBO getUserAuthRes(User user){
        UserRoleInfo userRoleInfo = userRoleInfoService.getOne(new LambdaQueryWrapper<UserRoleInfo>().eq(UserRoleInfo::getUserId, user.getId()));
        RoleInfo role = roleInfoService.getById(userRoleInfo.getRoleId());

        UserAuthBO authBO = new UserAuthBO();
        UserModel userModel = BeanCopierUtil.copyOne(user, UserModel.class);
        RoleModel roleModel = BeanCopierUtil.copyOne(role, RoleModel.class);
        OrgModel orgModel = orgInfoService.getOrgModelById(role.getOrgInfoId());

        assert roleModel != null;
        List<SourceInfo> sourceInfos = sourceInfoService.getOpenAndHalfOpenSources();
        List<SourceRoleControl> sourceRoleControlList = sourceRoleControlService.getSourceRoleControlList(roleModel.getId());
        List<SourceOrgControl> sourceOrgControlList = sourceOrgControlService.getSourceOrgControlList(orgModel.getId());
        Set<Integer> sourceIds = new HashSet<>();

        sourceIds.addAll(sourceRoleControlList.stream().map(SourceRoleControl::getSourceId).collect(Collectors.toSet()));
        sourceIds.addAll(sourceOrgControlList.stream().map(SourceOrgControl::getSourceId).collect(Collectors.toSet()));
        if(!sourceIds.isEmpty()){
            List<SourceInfo> notOpenSources = sourceInfoService.listByIds(sourceIds);
            sourceInfos.addAll(notOpenSources);
        }

        authBO.setUserModel(userModel);
        authBO.setRoleModel(roleModel);
        authBO.setOrgModel(orgModel);
        authBO.setSourceInfos(sourceInfos);
        return authBO;
    }



    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public void setJwtTokenUtil(JwtTokenUtil jwtTokenUtil) {
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Autowired
    public void setOrgInfoService(OrgInfoService orgInfoService) {
        this.orgInfoService = orgInfoService;
    }

    @Autowired
    public void setRoleInfoService(RoleInfoService roleInfoService) {
        this.roleInfoService = roleInfoService;
    }

    @Autowired
    public void setSourceRoleControlService(SourceRoleControlService sourceRoleControlService) {
        this.sourceRoleControlService = sourceRoleControlService;
    }

    @Autowired
    public void setSourceOrgControlService(SourceOrgControlService sourceOrgControlService) {
        this.sourceOrgControlService = sourceOrgControlService;
    }

    @Autowired
    public void setUserRoleInfoService(UserRoleInfoService userRoleInfoService) {
        this.userRoleInfoService = userRoleInfoService;
    }

    @Autowired
    public void setSourceInfoService(SourceInfoService sourceInfoService) {
        this.sourceInfoService = sourceInfoService;
    }

    @Autowired
    public void setMenuService(MenuService menuService) {
        this.menuService = menuService;
    }
}
